package com.gitee.wsl.data.store

import com.gitee.wsl.data.cache.coroutines.KmpKvStoreRollbackException
import com.gitee.wsl.data.observer.KmpKvStoreObserverRegistry
import com.gitee.wsl.io.api.getOrNull
import com.gitee.wsl.reflect.TypeInfo
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.locks.reentrantLock
import kotlinx.atomicfu.locks.withLock
import kotlinx.atomicfu.update

abstract class AbstractStoreServer:StoreServer {

    private val transaction = atomic<Map<String, Any?>?>(null)
    private val transactionLock = reentrantLock()


    protected abstract fun realRemove(key: String)

    protected abstract fun <T:Any> realPut(key: String, value: T)

    override fun remove(key: String){
        if(isExits(key)) {
            realRemove(key)
            KmpKvStoreObserverRegistry.notifyValueRemove(nodeId, key)
        }
    }

    override fun <T:Any> put(key: String, value: T) {
        val old = getOrNull<T>(key, TypeInfo(value::class))
        if (transaction.value != null) transaction.update { it?.toMutableMap()?.apply { put(key, old) } }
        realPut(key,value)
        KmpKvStoreObserverRegistry.notifyValueChange(nodeId,key,old,value)
    }

    fun transaction(block: (rollback: () -> Nothing) -> Unit) {
        transactionLock.withLock {
            try {
                transaction.update { mutableMapOf() }
                val rollback = { throw KmpKvStoreRollbackException()  }
                block(rollback)
            } catch (_: Exception) {
                transaction.value?.forEach { (k, v) ->
                    if (v == null) {
                        remove(k)
                    } else {
                        put(k, v)
                    }
                }
                transaction.update { null }
            }
        }
    }

}

abstract class AbstractReadOnlyStoreServer:StoreServer {

    override fun remove(key: String){}

    override fun <T:Any> put(key: String, value: T) {}

}